package hasthi.wsrf;

import hasthi.common.ChangeListener;
import hasthi.common.HasthiException;
import hasthi.common.XmlBeanUtils;
import hasthi.common.constants.HasthiConstants;
import hasthi.wsrf.faults.InvalidResourcePropertyQNameFault;

import java.util.HashMap;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlbeans.XmlCursor;
import org.w3.x2005.x08.addressing.EndpointReferenceType;

import wsrf.DeleteResourcePropertiesDocument;
import wsrf.DeleteResourcePropertiesResponseDocument;
import wsrf.DeleteType;
import wsrf.GetMultipleResourcePropertiesDocument;
import wsrf.GetMultipleResourcePropertiesResponseDocument;
import wsrf.GetResourcePropertyDocument;
import wsrf.GetResourcePropertyDocumentResponseDocument;
import wsrf.GetResourcePropertyResponseDocument;
import wsrf.InsertResourcePropertiesDocument;
import wsrf.InsertResourcePropertiesResponseDocument;
import wsrf.InsertType;
import wsrf.SetResourcePropertiesDocument;
import wsrf.SetResourcePropertiesResponseDocument;
import wsrf.UpdateResourcePropertiesDocument;
import wsrf.UpdateResourcePropertiesResponseDocument;
import wsrf.UpdateType;
import wsrf.GetMultipleResourcePropertiesResponseDocument.GetMultipleResourcePropertiesResponse;
import wsrf.GetResourcePropertyDocumentResponseDocument.GetResourcePropertyDocumentResponse;
import wsrf.GetResourcePropertyResponseDocument.GetResourcePropertyResponse;

public class ResourceManager {
    protected static Log log = LogFactory.getLog(HasthiConstants.AGENT_LOGGER_NAME);
    private static enum EditAction {AddNew,Change,Delete};
    private WSResourceLocater resourceLocater;
    private ChangeListener resourceListener;
    
    public ResourceManager(WSResourceLocater resourceLocater) {
        this.resourceLocater = resourceLocater;
    }

    public GetResourcePropertyDocumentResponseDocument getResourcePropertyDocument(
            EndpointReferenceType epr) throws HasthiException {
        WSResource resource = resourceLocater.findResource(epr);
        GetResourcePropertyDocumentResponseDocument responseDocument = GetResourcePropertyDocumentResponseDocument.Factory
                .newInstance();
        GetResourcePropertyDocumentResponse responseType = responseDocument
                .addNewGetResourcePropertyDocumentResponse();

        XmlCursor c = responseType.newCursor();
        c.toNextToken();
        c.beginElement(new QName(resource.getName().getNamespaceURI(), resource.getName()
                .getLocalPart()
                + "Propereties"));

        HashMap<QName, Object> resourceProperties = resource.getResourceProperties();
        for (QName name : resourceProperties.keySet()) {
            Object value = resourceProperties.get(name);
            XmlBeanUtils.addNameValuePair(c, name, value);
        }
        c.dispose();
        return responseDocument;
    }

    public GetResourcePropertyResponseDocument getResourceProperty(EndpointReferenceType epr,
            GetResourcePropertyDocument document) throws HasthiException {
        WSResource resource = resourceLocater.findResource(epr);

        QName name = document.getGetResourceProperty();
        Object value = resource.getResourceProperty(name);
        if(value == null){
            throw new InvalidResourcePropertyQNameFault(epr,name," Property Does not Exists");
        }

        GetResourcePropertyResponseDocument responseDocument = GetResourcePropertyResponseDocument.Factory
                .newInstance();
        GetResourcePropertyResponse resourcePropertyResponse = responseDocument
                .addNewGetResourcePropertyResponse();
        XmlCursor c = resourcePropertyResponse.newCursor();
        c.toNextToken();
        XmlBeanUtils.addNameValuePair(c, name, value);
        c.dispose();
        return responseDocument;
    }

    public GetMultipleResourcePropertiesResponseDocument getMultipleResourceProperties(
            EndpointReferenceType epr, GetMultipleResourcePropertiesDocument document)
            throws HasthiException {
        WSResource resource = resourceLocater.findResource(epr);

        GetMultipleResourcePropertiesResponseDocument responseDocument = GetMultipleResourcePropertiesResponseDocument.Factory
                .newInstance();
        GetMultipleResourcePropertiesResponse resourcePropertyResponse = responseDocument
                .addNewGetMultipleResourcePropertiesResponse();
        XmlCursor c = resourcePropertyResponse.newCursor();
        c.toNextToken();

        QName[] propertiesRequested = document.getGetMultipleResourceProperties()
                .getResourcePropertyArray();
        for (QName property : propertiesRequested) {
            Object value = resource.getResourceProperty(property);
            XmlBeanUtils.addNameValuePair(c, property, value);
        }
        c.dispose();
        return responseDocument;
    }

    public InsertResourcePropertiesResponseDocument insertResourceProperties(
            EndpointReferenceType epr, InsertResourcePropertiesDocument document) throws WsrfException {
        WSResource resource = resourceLocater.findResource(epr);
        InsertType action = document.getInsertResourceProperties().getInsert();
        XmlCursor c = action.newCursor();
        c.toNextToken();
        QName name = c.getName();
        
        Object value = XmlBeanUtils.readValueOffXml(c);
        
//        
//        
//        
//        
//        c.toNextToken();
//        
//        Object value;
//        if (c.isText()) {
//            value = c.getTextValue();
//        } else {
//            c.toParent();
//            value = c.getTextValue();
//        }
        resource.insertResourceProperties(name, value);
        c.dispose();
        
        InsertResourcePropertiesResponseDocument responseDocument =  InsertResourcePropertiesResponseDocument.Factory.newInstance();
        responseDocument.addNewInsertResourcePropertiesResponse();
        notifyListeners(EditAction.AddNew, resource.getName(), name, value);
        log.debug("Add property to "+ resource.getName() + " name = "+ name + " value = "+ value);
        return responseDocument;
    }
    
    public UpdateResourcePropertiesResponseDocument updateResourceProperties(EndpointReferenceType epr,UpdateResourcePropertiesDocument document)throws WsrfException {
        WSResource resource = resourceLocater.findResource(epr);
        
        UpdateType action = document.getUpdateResourceProperties().getUpdate();
        XmlCursor c = action.newCursor();
        c.toNextToken();
        QName name = c.getName();
//        c.toNextToken();
//        Object value;
//        if (c.isText()) {
//            value = c.getTextValue();
//        } else {
//            c.toParent();
//            value = c.getTextValue();
//        }
        Object value = XmlBeanUtils.readValueOffXml(c);
        resource.updateResourceProperties(name, value);
        c.dispose();
        
        

        UpdateResourcePropertiesResponseDocument responseDocument =  UpdateResourcePropertiesResponseDocument.Factory.newInstance();
        responseDocument.addNewUpdateResourcePropertiesResponse();
        notifyListeners(EditAction.Change, resource.getName(), name, value);
        log.debug("Update property to "+ resource.getName() + " name = "+ name + " value = "+ value);
        return responseDocument;
    }
    
    public SetResourcePropertiesResponseDocument setResourceProperties(EndpointReferenceType epr,SetResourcePropertiesDocument document)throws WsrfException {
        //We do not do this for need of transactional memory
        throw new UnsupportedOperationException();
    }
    
    public DeleteResourcePropertiesResponseDocument deleteResourceProperties(EndpointReferenceType epr,DeleteResourcePropertiesDocument document)throws WsrfException {
        WSResource resource = resourceLocater.findResource(epr);
        DeleteType action = document.getDeleteResourceProperties().getDelete();
        resource.deleteResourceProperty(action.getResourceProperty());
        DeleteResourcePropertiesResponseDocument responseDocument =  DeleteResourcePropertiesResponseDocument.Factory.newInstance();
        responseDocument.addNewDeleteResourcePropertiesResponse();
        notifyListeners(EditAction.Delete, resource.getName(), action.getResourceProperty(), null);
        log.debug("Update property to "+ resource.getName() + " name = "+ action.getResourceProperty());
        return responseDocument;
    }
    
    
    
    
    
    

//    public SetResourcePropertiesResponseDocument setResourceProperties(EndpointReferenceType epr,
//            SetResourcePropertiesDocument document) throws WsrfException {
//        WSResource resource = resourceLocater.findResource(epr);
//        DeleteType[] deleteActions = document.getSetResourceProperties().getDeleteArray();
//        for (DeleteType action : deleteActions) {
//            resource.deleteResourceProperty(action.getResourceProperty());
//        }
//
//        UpdateType[] updateActions = document.getSetResourceProperties().getUpdateArray();
//        for (UpdateType action : updateActions) {
//            XmlCursor c = action.newCursor();
//            c.toNextToken();
//            QName name = c.getName();
//            if (c.isText()) {
//                resource.updateResourceProperties(name, c.getTextValue());
//            } else {
//                c.toFirstChild();
//                resource.updateResourceProperties(name, c.getObject());
//            }
//        }
//
//        InsertType[] insertActions = document.getSetResourceProperties().getInsertArray();
//
//        for (InsertType action : insertActions) {
//            XmlCursor c = action.newCursor();
//            c.toNextToken();
//            QName name = c.getName();
//            if (c.isText()) {
//                resource.insertResourceProperties(name, c.getTextValue());
//            } else {
//                c.toFirstChild();
//                resource.insertResourceProperties(name, c.getObject());
//            }
//        }
//        SetResourcePropertiesResponseDocument responseDocument =  SetResourcePropertiesResponseDocument.Factory.newInstance();
//        responseDocument.addNewSetResourcePropertiesResponse();
//        return responseDocument;
//    }
    
    
   
    
    
    protected void notifyListeners(EditAction action,QName resourceName,QName propertyName,Object newValue){
        if(resourceListener != null){
            switch(action){
                case AddNew:
                    resourceListener.propertyAdded(resourceName, propertyName);
                case Change:
                    resourceListener.propertyChanged(resourceName, propertyName, newValue);
                default:
                    resourceListener.propertyRemoved(resourceName, propertyName);
            }
        }
    }

    public void setResourceListener(ChangeListener resourceListener) {
        this.resourceListener = resourceListener;
    }

}
